package trb.complexshield.bool.visualisation;

import com.sun.j3d.utils.geometry.GeometryInfo;
import com.sun.j3d.utils.geometry.NormalGenerator;
import java.util.ArrayList;
import java.util.List;
import javax.media.j3d.Appearance;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.Material;
import javax.media.j3d.Shape3D;
import javax.media.j3d.TriangleArray;
import javax.vecmath.Color4f;
import javax.vecmath.Point3f;
import no.hrp.hvrc.rad.vis.RadiationGrid;
import no.hrp.hvrc.rad.vis.marchingcube.ColoredTriangleMeshGenerator;
import no.hrp.hvrc.rad.vis.marchingcube.Cube;
import no.hrp.hvrc.utils.j3d.BBox;
import no.hrp.hvrc.utils.j3d.ObjectViewer;
import no.hrp.hvrc.utils.j3d.SGUtils;
import no.hrp.hvrc.utils.j3d.Vec3;
import trb.complexshield.benchmarking.SegmentsBenchmark;
import trb.complexshield.bool.distance.DistanceProducer;
import trb.complexshield.bool.model.AABBCreator;
import trb.complexshield.bool.model.DistanceCreator;

public class DistanceVisualisation {

    private BBox aabb;
    private DistanceProducer distanceProducer;

    public DistanceVisualisation(BBox aabb, DistanceProducer distanceProducer) {
        this.aabb = BBox.fromCenterAndSize(aabb.getCenter(), aabb.getSize().scale(1.2));
        this.distanceProducer = distanceProducer;
    }

    public BranchGroup update() {
        RadiationGrid grid = new RadiationGrid(aabb.getLower(), aabb.getUpper(), aabb.getSize().max() / 75);
        float[] coordsRef = grid.createCoordsRef();
        float[] values = new float[coordsRef.length / 3];
        for (int i=0; i<coordsRef.length; i+=3) {
            float x = coordsRef[i];
            float y = coordsRef[i+1];
            float z = coordsRef[i+2];
            values[i/3] = distanceProducer.getDistance(new Vec3(x, y, z));
        }
        Cube cube = new Cube();
        cube.color = new Color4f(1, 1, 1, 1);
        ColoredTriangleMeshGenerator creator = new ColoredTriangleMeshGenerator();
        for (RadiationGrid.Cell cell : grid.getCellsRef()) {
            cube.initCorners(cell, values);
            cube.generate(0f, creator);
        }
        Shape3D newShape = new Shape3D();
        for (GeometryArray geometryArray : createGeometryArrays(creator.verts, creator.vertexCount)) {
            GeometryInfo geometryInfo = new GeometryInfo(geometryArray);
            NormalGenerator normalGenerator = new NormalGenerator();
            normalGenerator.generateNormals(geometryInfo);
            newShape.addGeometry(geometryInfo.getIndexedGeometryArray());
        }
        Appearance appearance = new Appearance();
        appearance.setMaterial(new Material());
        newShape.setAppearance(appearance);
        return SGUtils.wrapInBrancGroup(newShape);
    }

    static List<GeometryArray> createGeometryArrays(Point3f[] verts, int vertexCount) {
        List<GeometryArray> list = new ArrayList<GeometryArray>();
        int vertexIdx = 0;
        while (vertexIdx < vertexCount) {
            int vertexFormat = GeometryArray.COORDINATES;
            // DirectX only supports 0xffff number of indices. Split into different geometry if necessary.
            int safeCount = Math.min(vertexCount - vertexIdx, 0xffff / 3 * 3);
            TriangleArray triArray = new TriangleArray(safeCount, vertexFormat);
            triArray.setCoordinates(0, verts, vertexIdx, safeCount);
            vertexIdx += vertexCount;
            list.add(triArray);
        }
        return list;
    }

    public static void main(String[] args) {
        Object boolModel = SegmentsBenchmark.createBoolModel();
        DistanceVisualisation distanceVisualisation = new DistanceVisualisation(
                AABBCreator.createAABB(boolModel), DistanceCreator.createDistanceProducer(boolModel));
        BranchGroup bg = distanceVisualisation.update();
        ObjectViewer.view(bg);
    }
}
